package com.example.demo.filter;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.isAlreadyRouted;

import com.alibaba.fastjson.JSON;
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreaker.State;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.SlidingWindowType;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class FallbackGatewayFilter implements GatewayFilter, Ordered {

  private static final Logger logger = LoggerFactory.getLogger(FallbackGatewayFilter.class);

  /**
   * 这里的值比为 10100 - 1 其中，10100 是 LoadBalancerClientFilter 的 Order, 这里比其提前执行
   */
  public static final int ORDER = 10000 + 1;

  @Override
  public int getOrder() {
    return ORDER;
  }

  public static Map<String, CircuitBreaker> circuitBreakers = new HashMap<>();

  private void registerCircuitBreaker(String command, long timeoutMills) {
    CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
        .failureRateThreshold(75)  // 配置失败率阈值，以百分比表示。
        .slowCallRateThreshold(80)
        .slowCallDurationThreshold(Duration.ofMillis(timeoutMills))
        .permittedNumberOfCallsInHalfOpenState(5)
        .maxWaitDurationInHalfOpenState(Duration.ofSeconds(120))
        .slidingWindowType(SlidingWindowType.COUNT_BASED)
        .slidingWindowSize(20)
        .minimumNumberOfCalls(5)
        .waitDurationInOpenState(Duration.ofSeconds(30))
        .automaticTransitionFromOpenToHalfOpenEnabled(true)
        .build();

    circuitBreakers.put(command, CircuitBreaker.of(command, circuitBreakerConfig));
  }

  /**
   * timeout_config = { "a": 0.1, "b": 0.2, "c": 0.3, "d": 0.4, "e": 0.5, }
   */
  FallbackGatewayFilter() {
    registerCircuitBreaker("a", 100);
    registerCircuitBreaker("b", 200);
    registerCircuitBreaker("c", 300);
    registerCircuitBreaker("d", 400);
    registerCircuitBreaker("e", 500);
  }

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    if (isAlreadyRouted(exchange)) {
      return chain.filter(exchange);
    }

    String requestBody = exchange.getAttribute(ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR);
    String command = JSON.parseObject(requestBody).getString("command");

    CircuitBreaker circuitBreaker = circuitBreakers.get(command);
    if (circuitBreaker == null) {
      return chain.filter(exchange);
    }

    logger.warn("{} circuitBreaker {}", command, circuitBreaker.getState());

    if (circuitBreaker.getState() == State.OPEN) {
      // 熔断降级
      ServerHttpResponse response = exchange.getResponse();
      response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);
      DataBuffer bodyDataBuffer = response.bufferFactory().wrap("{}".getBytes());
      response.setStatusCode(HttpStatus.OK);
      return response.writeWith(Mono.just(bodyDataBuffer));
    }
    else if (circuitBreaker.getState() == State.HALF_OPEN) {
      // 半开测试
      return chain.filter(exchange);
    }
    else {
      // 正常测试
      return chain.filter(exchange);
    }
  }
}
